#include "FtpCommandParser.h"

#include <sstream>
#include <algorithm>

using namespace std;

FtpCommandParser::FtpCommandEntry FtpCommandParser::commandList[] =
{
	{"USER", &FtpCommandParser::OnUser},
	{"PASS", &FtpCommandParser::OnPass},
	{"LIST", &FtpCommandParser::OnList},
	{"SYST", &FtpCommandParser::OnSyst},
	{"PORT", &FtpCommandParser::OnPort},
	{"CWD", &FtpCommandParser::OnCwd},
	{"PWD", &FtpCommandParser::OnPwd},
	{"RMD", &FtpCommandParser::OnRmd},
	{"MKD", &FtpCommandParser::OnMkd},
	{"DELE", &FtpCommandParser::OnDele},
	{"CDUP", &FtpCommandParser::OnCdup},
	{"RETR", &FtpCommandParser::OnRetr},
	{"STOR", &FtpCommandParser::OnStor},
	{"NOOP", &FtpCommandParser::OnNoop},
	{"QUIT", &FtpCommandParser::OnQuit},
	{"TYPE", &FtpCommandParser::OnType},
};

FtpCommandParser::FtpCommandParser(): observer(0)
{
}

FtpCommandParser::~FtpCommandParser()
{
}

unsigned int FtpCommandParser::CommandCount() const
{
	return sizeof(commandList)/sizeof(commandList[0]);
}

void FtpCommandParser::Parse(const std::string& line)
{
	string command = line;
	string arguments;
	string::size_type index;

	// Search for <SP>
	index = line.find_first_of(' ');

	// Found <SP>
	if (index != string::npos)
	{
		// Extract command
		command = line.substr(0, index);

		// Extract arguments after <SP>
		index = line.find_first_not_of(' ', index);
		if (index != string::npos)
		{
			arguments = line.substr(index, line.length()-index);
		}
	}

	// Convert command to uppercase
	std::transform(command.begin(), command.end(), command.begin(), toupper);

	printf("Command: \"%s\"\n", command.c_str());
	printf("Arguments: \"%s\"\n", arguments.c_str());

	bool commandFound = false;

	for(unsigned int i=0; i<CommandCount(); i++)
	{
		if(commandList[i].command == command)
		{
			void (FtpCommandParser::*handler)(const std::string& arg) = commandList[i].handler;

			commandFound = true;
			(this->*handler)(arguments);
		}
	}

	if (!commandFound)
	{
		if (observer)
			observer->OnUnknownCommand();
	}
}

void FtpCommandParser::RegisterObserver(FtpCommandObserver* newObserver)
{
	observer = newObserver;
}

void FtpCommandParser::RemoveObserver()
{
	observer = 0;
}

void FtpCommandParser::OnUser(const std::string& arg)
{
	if (observer)
	{
		// USER must have an argument

		if (arg.length())
			observer->OnUser(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnPass(const std::string& arg)
{
	if (observer)
	{
		// PASS must have an argument

		if (arg.length())
			observer->OnPass(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnList(const std::string& arg)
{
	if (observer)
	{
		// LIST may have an argument
		observer->OnList(arg);
	}
}

void FtpCommandParser::OnSyst(const std::string& arg)
{
	if (observer)
	{
		// SYST must not have an argument
		if (arg.length() == 0)
			observer->OnSyst();
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnPort(const std::string& arg)
{
	bool valid = false;
	unsigned int portnumbers[6];
	unsigned int numbercount = 0;
	unsigned int worknumber;
	std::string numberstr;
	std::istringstream commasplitter(arg);

	// Split arguments via comma
	while(!commasplitter.eof() && !commasplitter.fail() && numbercount < 6)
	{
		getline(commasplitter, numberstr, ',');

		std::istringstream numberconverter(numberstr);

		// Convert string to number, make sure it used up all characters
		if (!(numberconverter >> worknumber) || !numberconverter.eof() || numberconverter.fail())
			break;

		if (worknumber > 255)
			break;

		portnumbers[numbercount++] = worknumber;

		if (numbercount == 6 && commasplitter.eof())
		{
			valid = true;
		}
	}

	if (observer)
	{
		if (valid)
		{	
			std::ostringstream ip;
			unsigned short port = (portnumbers[4]<<8) | portnumbers[5];

			ip << portnumbers[0] << "." << portnumbers[1] << "." << portnumbers[2] << "." << portnumbers[3];
			observer->OnPort(ip.str(), port);
		}
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnCwd(const std::string& arg)
{
	if (observer)
	{
		// CWD must have argument

		if (arg.length())
			observer->OnCwd(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnType(const std::string& arg)
{
	if (observer)
	{
		// TYPE must have argument

		if (arg.length())
			observer->OnType(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnRetr(const std::string& arg)
{
	if (observer)
	{
		// RETR must have argument

		if (arg.length())
			observer->OnRetr(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnStor(const std::string& arg)
{
	if (observer)
	{
		// STOR must have argument

		if (arg.length())
			observer->OnStor(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnPwd(const std::string& arg)
{
	if (observer)
	{
		// PWD may not have an argument

		if (!arg.length())
			observer->OnPwd();
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnNoop(const std::string& arg)
{
	if (observer)
	{
		// NOOP may not have an argument

		if (!arg.length())
			observer->OnNoop();
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnQuit(const std::string& arg)
{
	if (observer)
	{
		// QUIT may not have an argument

		if (!arg.length())
			observer->OnQuit();
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnRmd(const std::string& arg)
{
	if (observer)
	{
		// RMD must have argument

		if (arg.length())
			observer->OnRmd(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnMkd(const std::string& arg)
{
	if (observer)
	{
		// MKD must have argument

		if (arg.length())
			observer->OnMkd(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnDele(const std::string& arg)
{
	if (observer)
	{
		// DELE must have argument

		if (arg.length())
			observer->OnDele(arg);
		else
			observer->OnSyntaxError();
	}
}

void FtpCommandParser::OnCdup(const std::string& arg)
{
	if (observer)
	{
		// CDUP may not have an argument

		if (!arg.length())
			observer->OnCdup();
		else
			observer->OnSyntaxError();
	}
}

